package com.idea.relax.redis.lock.aspect;

import com.idea.relax.redis.lock.client.RedisLockClient;
import com.idea.relax.redis.lock.support.LockType;
import com.idea.relax.redis.lock.annotation.RedisLock;
import com.idea.relax.redis.lock.config.RelaxRedisLockProperties;
import com.idea.relax.redis.support.spel.EvaluationContextRootObject;
import com.idea.relax.redis.support.spel.IExpressionEvaluator;
import com.idea.relax.redis.support.spel.RelaxExpressionEvaluator;
import com.idea.relax.redis.support.utils.RelaxUtil;
import lombok.AllArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.Assert;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @className: RedisLockAspect
 * @description: 分布式锁的切面类
 * @author: salad
 * @date: 2022/9/2
 **/
@Aspect
@AllArgsConstructor
public class RedisLockAspect {

    private final RelaxRedisLockProperties properties;

    private final IExpressionEvaluator expressionEvaluator;

    private final RedisLockClient redisLockClient;


    @Around("@annotation(redisLock)")
    public Object aroundRedisLock(ProceedingJoinPoint point, RedisLock redisLock) {
        String lockName = redisLock.value();
        String keySuf = redisLock.param();
        Assert.hasText(lockName, "请指定一个lock的名称！");
        LockType lockType = redisLock.type();
        long waitTime = redisLock.waitTime();
        long leaseTime = redisLock.leaseTime();
        TimeUnit timeUnit = redisLock.timeUnit();
        String key;
        if (RelaxUtil.isNotBlank(properties.getLockPrefix())) {
            key = properties.getLockPrefix() + ":" + lockName;
        } else {
            key = lockName;
        }
        if (RelaxUtil.isNotBlank(keySuf)) {
            key  += ":" + resolveParam(point, keySuf);
        }
        return redisLockClient.lock(key, lockType, waitTime, leaseTime, timeUnit, point::proceed);
    }

    private String resolveParam(ProceedingJoinPoint point, String keySuf) {
        EvaluationContextRootObject rootObject = new EvaluationContextRootObject();
        rootObject.setClassName(Objects.requireNonNull(point.getTarget()).getClass().getSimpleName());
        MethodSignature ms = (MethodSignature) point.getSignature();
        rootObject.setMethodName(ms.getMethod().getName());
		rootObject.setMethod(ms.getMethod());
        rootObject.setTargetClass(point.getTarget().getClass());
        rootObject.setTarget(point.getTarget());
        rootObject.setArgs(point.getArgs());
        EvaluationContext context = expressionEvaluator.createContext(rootObject, true);
        AnnotatedElementKey elementKey = new AnnotatedElementKey(rootObject.getMethod(), rootObject.getTargetClass());
        return expressionEvaluator.evalAsText(keySuf, elementKey, context);
    }

}
